use async_trait::async_trait;
use base_data::channel::EndReason;
use tools::{error::Error, ResultNull};
use tracing::{debug, error, info};

use crate::{
	business::{
		bus_trait::Business,
		inbound400::{
			bus_all_call::SaveCalleds,
			context::{BusinessState, InboundRunContext},
		},
	},
	config::business_config,
	db::persistence::InboundDBO,
	link_to_sip::play_sound_file_to_caller,
};

use super::talk::Talk;

#[derive(Debug, Clone)]
pub struct CallTips;

#[async_trait]
impl Business for CallTips {
	type C = InboundRunContext;

	async fn start_call(&self, context: &mut InboundRunContext) -> ResultNull {
		debug!("开始启动播报来电提示处理..当前参数:{:?}", &self);

		context.start_charge();
		//此值在这个操作节点做为判断两边是否都播放完成,以位表示.值1:播放完成
		//位0:a-leg播放完成,位1:b-leg播放完成
		context.temp_count = 0;
		//向主叫播放主叫的提示音
		play_sound_file_to_caller(
			context.caller_channel(),
			business_config().seat_warm_caller_bell_path,
		)?;

		//向被叫播放被叫提示音
		if let Some(o_ch) = context.called_channel() {
			play_sound_file_to_caller(o_ch, business_config().seat_warm_called_bell_path)?;
		} else {
			error!("被叫通道为空,无法播放被叫提示音")
		}

		Ok(())
	}

	async fn play_back_stop(
		&self,
		context: &mut InboundRunContext,
		channel_id: u32,
	) -> ResultNull {
		if channel_id == context.caller_id() {
			context.temp_count |= 1;
		} else {
			context.temp_count |= 2;
		}

		//两边都播放完成。转通话节点处理
		if context.temp_count == 3 {
			context.next_handler = Some(&Talk);
			Err(Error::ReplaceNode)
		} else {
			Ok(())
		}
	}

	async fn hangup(
		&self,
		context: &mut InboundRunContext,
		channel_id: u32,
		reason: EndReason,
	) -> ResultNull {
		info!("收到挂断消息,id:{channel_id},原因:{reason:?}");

		//收到挂断消息，同时清除全局呼叫记录
		SaveCalleds::remove_called(&context.called_num()).await;

		//有时候送过来的挂断是之前呼叫进行的挂断。
		if !(channel_id == context.caller_id() || channel_id == context.called_id()) {
			debug!("这个挂断消息非当前呼入呼出通道的,忽略");
			return Ok(());
		}

		//当收到挂断消息，因为这里还未设置通话状态。但已经开始计费。没有录音
		context.business_state = BusinessState::End(EndReason::TalkEnd);

		Ok(())
	}

	async fn closeing(&self, context: &mut InboundRunContext) -> ResultNull {
		let r = match &context.business_state {
			BusinessState::End(r) => r.clone(),
			_ => EndReason::UnknownValue,
		};

		//计费
		if context.is_charging() {
			InboundDBO::record_and_charge(
				context,
				&context.called_num(),
				&context.rec_file_name,
				r,
				context.node_info.node_id,
				&context.dtmf,
				"",
			)
			.await?;
		}

		Ok(())
	}
}
